package cn.com.dhcc.turbo.service.registry.sync.webservice;

import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import cn.com.dhcc.app.core.CM;
import cn.com.dhcc.app.core.exception.DaoException;
import cn.com.dhcc.app.core.exception.NiceException;
import cn.com.dhcc.app.core.exception.ServiceException;
import cn.com.dhcc.app.core.service.BaseService;
import cn.com.dhcc.app.core.service.Log;
import cn.com.dhcc.app.core.service.Log.LOGTYPE;
import cn.com.dhcc.app.core.tag.CommonStaticCode.SUCCESSFAIL;
import cn.com.dhcc.app.core.tag.CommonStaticCode.YESNO;
import cn.com.dhcc.app.core.util.CollectionUtil;
import cn.com.dhcc.app.core.util.JsonUtil;
import cn.com.dhcc.app.core.util.UUIDGenerator;
import cn.com.dhcc.app.core.util.UrlUtil;
import cn.com.dhcc.app.pub.core.consts.AppConst;
import cn.com.dhcc.turbo.entity.registry.DomainInfo;
import cn.com.dhcc.turbo.entity.registry.ExchConnRequest;
import cn.com.dhcc.turbo.entity.registry.NodeInfo;
import cn.com.dhcc.turbo.entity.registry.NodePipeline;
import cn.com.dhcc.turbo.entity.registry.SynchronizeInfo;
import cn.com.dhcc.turbo.entity.registry.SynchronizeInfo.SyncOperType;
import cn.com.dhcc.turbo.entity.registry.SynchronizeInfo.SyncStatus;
import cn.com.dhcc.turbo.entity.registry.SynchronizeInfo.SyncType;
import cn.com.dhcc.turbo.service.exch.exhmgr.ExchConnRequestService;
import cn.com.dhcc.turbo.service.registry.LocalConfCallBack;
import cn.com.dhcc.turbo.service.registry.vo.NodeExchRelResponse;
import cn.com.dhcc.turbo.service.registry.vo.NodeExchRelVo;
/**
 * 
 * @author qiao
 * @2014-5-30 @上午10:01:45
 */
@Service
public class NodeCommunicateClient extends BaseService{
	
	/**
	 * 系统调用
	 * @author qiao
	 * @2014-5-16 @下午4:03:47
	 */
	@Autowired
	NodeCommunicateService communicateService;
	@Autowired
	LocalConfCallBack localConfCallBack; 
	@Autowired
	ExchConnRequestService connRequestService;
	@SuppressWarnings("deprecation")
	private static Logger logger = Log.getExistLogger(LOGTYPE.SYS);
	
	/**
	 * 发起建立交换关系请求
	 * @param nodeInstallUrl
	 * @throws ServiceException
	 * @throws NiceException
	 */
	@SuppressWarnings("unused")
	public void sendExchRelRequest(String nodeInstallUrl)throws ServiceException, NiceException{
		if(StringUtils.hasText(nodeInstallUrl)){
			nodeInstallUrl = UrlUtil.removeSlashAndBlack(nodeInstallUrl);
		}
		//验证地址
		checkInstallUrl(nodeInstallUrl);
		//验证
	    NodeExchRelVo connRequest = null;//communicateService.getlocalNeedSyncInfo();
		NodeInfo localNode =null;// connRequest.getFromNode(); 
		//验证节点是否已经初始化
		if(localNode==null){throw new NiceException("节点为初始化！");}
		List<NodePipeline> nodePipelines = connRequest.getPipeLines();
		//验证通道
		communicateService.checkLocalPipeLine(nodePipelines);
		//封装请求
		ExchConnRequest connRequestLocal = communicateService.getLocalWaitAuthRegistRequest(nodeInstallUrl);
		if(connRequestLocal==null){
			//将注册请求入库
			connRequestLocal = new ExchConnRequest();
			connRequestLocal.setId(UUIDGenerator.getUUID());
			connRequestLocal.setFromNodeId(localNode.getId());
			connRequestLocal.setToUrl(nodeInstallUrl.toLowerCase());
			connRequestLocal.setRequestType(ExchConnRequest.RequestType.EXCH_REL.getType());
			connRequestLocal.setStatus(ExchConnRequest.RequestStatus.WAIT.getCode());
			connRequestLocal.setRequestContent(JsonUtil.toJson(connRequest));
			connRequestLocal.setIsLocal(YESNO.YES.getStatus());
			connRequestLocal.setMtime(new Date());
		}
		logger.info("交换关系建立-本地：发送请求，内容：\r\n"+connRequest);
		NodeExchRelResponse rs = getRemoteNodeCommunicateService(nodeInstallUrl).receiveExchRelRequest(connRequest);
		try {
			CM.getDao().insertModule("插入请求", connRequestLocal);
			logger.info("交换关系建立-本地：插入请求");
		} catch (DaoException e) {
			throw new ServiceException(e);
		}
		logger.info("交换关系建立-本地：收到返回，内容：\r\n"+rs);
		connRequestLocal.setStatus(rs.getCode());
		connRequestLocal.setResponse(rs.getMsg());
		try {
			CM.getDao().updateModuleById("修改请求状态", connRequestLocal, null);
			logger.info("交换关系建立-本地：修改请求状态为--》"+connRequestLocal.getStatus());
		} catch (DaoException e) {
			throw new ServiceException(e);
		}
		if(SUCCESSFAIL.FAIL.getStatus().equalsIgnoreCase(rs.getCode())){
			logger.error("交换关系建立-本地：失败。原因："+rs.getMsg());
			throw new NiceException("建立请求失败!,"+rs.getMsg());
		}else{
			logger.info("交换关系建立-本地：成功。等待审核");
		}
	}
	


	/**
	 * 审核通过
	 * @param connRequest
	 * @throws ServiceException
	 * @throws NiceException 
	 */
	public void exchRelRequestPass(String exchConnRequestId) throws ServiceException, NiceException {
		//1 将请求发起的节点基本信息，通道信息，已注册的系统信息入库
		//2将本地节点的通道信息，节点基本信息，已注册的系统发给请求方
		String errorInfoNotBlock = null;
		ExchConnRequest connRequest = null;
		try {
			try {
				connRequest = communicateService.getExchRequest(exchConnRequestId);
			} catch (Exception e) {
				throw new NiceException("操作失败,系统错误...");
			}
			NodeInfo locaNodeInfo = communicateService.getLocalNodeInfo();
			NodeExchRelVo connectRequest = JsonUtil.toObject(connRequest.getRequestContent(),NodeExchRelVo.class); 
			NodeInfo fromNode = connectRequest.getFromNode();
			/*List<SysInfo> fromNodeSysInfos = connectRequest.getSysInfos();*/
			List<NodePipeline> fromNodePipeLines = connectRequest.getPipeLines();
			List<DomainInfo> domainInfos = connectRequest.getDomainInfos();
			NodeExchRelVo localRequest = communicateService.getlocalNeedSyncInfo();
			NodeExchRelResponse relReq = new NodeExchRelResponse();
			relReq.setData(localRequest);
			relReq.setCode(ExchConnRequest.RequestStatus.PASS.getCode());
			NodeExchRelResponse exchRelResponse =  null;
			try {
				exchRelResponse =  getRemoteNodeCommunicateService(fromNode.getInstallUrl()).receiveConnectResponse(relReq);
			} catch (Exception e) {
				//请求失败，信息没有发过去，这个时候为了保持两边状态一致，两边都不修改状态
				throw new NiceException("操作失败,目的节点接收审核结果失败...");
			}
			if(SUCCESSFAIL.SUCCESS.getStatus().equals(exchRelResponse.getCode())){
				connRequest.setStatus(ExchConnRequest.RequestStatus.PASS.getCode()) ;
				connRequest.setResponse("通过");
				//插入同步的节点信息
				try {
					fromNode.setIsLocal(YESNO.NO.getStatus());
					communicateService.insertOrUpdateRemoteNode(fromNode);
					communicateService.insertOrUpdateRemotePipeLineInfo(fromNodePipeLines);
					//communicateService.insertOrUpdateRemoteSysInfo(fromNodeSysInfos);
					communicateService.insertOrUpdateRemoteDomains(domainInfos);
					//插入交换关系
					communicateService.insertExchRel(fromNode,locaNodeInfo);
					
				} catch (Exception e) {
					// TODO这个时候会出先两边状态不一致的情况 
					//exchRelResponse =  getRemoteNodeCommunicateService(fromNode.getInstallUrl()).deleteNodeRel(fromNode.getId(), locaNodeInfo.getId());
					throw new NiceException("操作失败,系统错误..");
				}
				//分配资源
				try {
					localConfCallBack.onNodeExchRelCreate(localRequest.getFromNode(), fromNode);
				} catch (Exception e) {
					errorInfoNotBlock = "交换关系建立成功，但分配通道资源失败，请手动处理...";
					logger.error(errorInfoNotBlock,e);
				}
				communicateService.updateRequestStatus(connRequest, ExchConnRequest.RequestStatus.PASS, StringUtils.hasText(errorInfoNotBlock)?errorInfoNotBlock:"通过");
			}else{
				communicateService.updateRequestStatus(connRequest, ExchConnRequest.RequestStatus.ERROR, exchRelResponse.getMsg());
			}
		} catch (RuntimeException e) {
			if(connRequest!=null){
				connRequest.setStatus(ExchConnRequest.RequestStatus.ERROR.getCode());
				connRequest.setResponse(e.getMessage());
			}
		}
		if(StringUtils.hasText(errorInfoNotBlock)){
			throw new NiceException(errorInfoNotBlock);
		}
	}
	/**
	 * 不通过
	 * @param connRequest
	 * @return
	 * @throws ServiceException
	 * @throws NiceException 
	 */
	public void exchRelRequestBlock(String exchConnRequestId,String blockMsg) throws ServiceException, NiceException {
		ExchConnRequest connRequest = communicateService.getExchRequest(exchConnRequestId);
		NodeExchRelVo connectRequest = JsonUtil.toObject(connRequest.getRequestContent(),NodeExchRelVo.class); 
		NodeInfo fromNode = connectRequest.getFromNode();
		
		//1 返回失败原因。
		//2求注册请求状态改为注册失败
		try {
			NodeExchRelVo localRequest = new NodeExchRelVo();;
			localRequest.setFromNode(communicateService.getLocalNodeInfo());
			NodeExchRelResponse relResponse = new NodeExchRelResponse();
			relResponse.setCode(ExchConnRequest.RequestStatus.BLOCK.getCode());
			relResponse.setMsg(blockMsg);
			relResponse.setData(localRequest);
			NodeExchRelResponse exchRelResponse =  getRemoteNodeCommunicateService(fromNode.getInstallUrl()).receiveConnectResponse(relResponse);
			if(SUCCESSFAIL.SUCCESS.getStatus().equals(exchRelResponse.getCode())){
				connRequest.setStatus(ExchConnRequest.RequestStatus.BLOCK.getCode()) ;
				connRequest.setResponse(blockMsg);
				logger.info("建立交换关系：成功发送不通过审批结果");
			}else{
				logger.error("建立交换关系：操作失败，"+exchRelResponse.getMsg());
				connRequest.setStatus(ExchConnRequest.RequestStatus.ERROR.getCode());
				connRequest.setResponse(exchRelResponse.getMsg());
				throw new NiceException(exchRelResponse.getMsg());
			}
			CM.getDao().updateModuleById("修改请求状态为不通过", connRequest,null);
		} catch (DaoException e) {
			connRequest.setStatus(ExchConnRequest.RequestStatus.ERROR.getCode());
			connRequest.setResponse(e.getMessage());
			throw new ServiceException(e);
		}
	}
	/**
	 * 修改本地节点基本信息,通道信息，系统信息时，同步给所有有交换关系的节点
	 * @param dataName 同步的数据名称：如：节点基本信息,通道信息，系统信息
	 * @param dataContent 单个对象的json串
	 * @param syncType
	 * @param operType
	 * @throws NiceException
	 * @throws ServiceException
	 */
	public void syncInfoToAll(String dataName,String dataContent, SyncType syncType,SyncOperType operType) throws NiceException, ServiceException {
		String sn = UUIDGenerator.getUUID();
		syncToAllRelNode(dataName,sn,dataContent,syncType,operType);
	}
	
	private void syncToAllRelNode(String desc,String sn,String dataContentJson,SyncType syncType,SyncOperType operType) throws NiceException, ServiceException {
		
		List<NodeInfo> hashRelNodes = communicateService.getHasRelNodes();
		if(CollectionUtil.hasElement(hashRelNodes)){
			Date mtime = new Date();
			NodeInfo localNode = communicateService.getLocalNodeInfo();
			int needToSize = hashRelNodes.size();
			int fail = 0;
			
			logger.info("开始同步"+desc+"给有交换关系的节点,需要同步的节点个数为->"+needToSize);
			for (Iterator<NodeInfo> iterator = hashRelNodes.iterator(); iterator
					.hasNext();) {
				
				NodeInfo remoteNode =  iterator.next();
				logger.info("开始同步"+desc+"给节点["+remoteNode.getNodeName()+"]");
				SynchronizeInfo synchronizeInfo = null;
				try {
					synchronizeInfo = new SynchronizeInfo();
					synchronizeInfo.setId(UUIDGenerator.getUUID());
					synchronizeInfo.setDataContent(dataContentJson);
					synchronizeInfo.setDataType(syncType.getCode());
					synchronizeInfo.setFromNode(localNode.getId());
					synchronizeInfo.setToNode(remoteNode.getId());
					synchronizeInfo.setOperType(operType.getCode());
					synchronizeInfo.setStatus(SyncStatus.ING.getName());
					synchronizeInfo.setMtime(mtime);
					synchronizeInfo.setSn(sn);
					synchronizeInfo.setFromNodeName(localNode.getNodeName());
					synchronizeInfo.setToNodeName(remoteNode.getNodeName());
					//同步
					syncToRelNode(desc, synchronizeInfo);
					
				} catch (Exception e) {
					logger.error(e);
					fail++;
					synchronizeInfo.setRemark(e.getMessage());
					onSyncError(synchronizeInfo);
				}
			}
			
			if(fail>0){
				logger.error("同步"+desc+"给有交换关系的节点 结束,失败个数->"+fail);
				throw new NiceException("部分信息同步失败,请手动同步!");
			}
			logger.info("同步"+desc+"给有交换关系的节点 结束");
		}
	}
	/**
	 * 同步给单个节点，手动
	 * @param desc
	 * @param synchronizeInfo
	 * @param remoteNode
	 * @throws NiceException
	 * @throws ServiceException
	 */
	public void syncToRelNode(final String desc,final SynchronizeInfo synchronizeInfo) throws NiceException, ServiceException {
		logger.info("开始同步线程...");
		Thread syncThread =  new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					startSync(desc, synchronizeInfo);
				} catch (Exception e) {
				} 
			}
		});
		syncThread.start();
	}
	private void startSync(String desc,SynchronizeInfo synchronizeInfo)throws NiceException, ServiceException{
		String remoteNodeId = synchronizeInfo.getToNode();
		NodeInfo remoteNode = communicateService.getNodeInfo(remoteNodeId);
		logger.info("开始同步"+desc+"给节点["+remoteNode.getNodeName()+"]");
		synchronizeInfo.setMtime(new Date());
		synchronizeInfo.setToNodeName(remoteNode.getNodeName());
		if(!communicateService.syncUrlIsValid(remoteNode.getInstallUrl())){
			logger.error("同步"+desc+"给节点["+remoteNode.getNodeName()+"]失败，同步地址["+remoteNode.getInstallUrl()+"]连接失败！");
			throw new NiceException("同步"+desc+"给节点["+remoteNode.getNodeName()+"]失败，同步地址["+remoteNode.getInstallUrl()+"]连接失败！");
		}
		NodeExchRelResponse response = 	null;
		try {
			response = getRemoteNodeCommunicateService(remoteNode.getInstallUrl()).receiveSyncInfo(synchronizeInfo);
		} catch (Exception e) {
			logger.error("同步"+desc+"给节点["+remoteNode.getNodeName()+"]失败",e);
			synchronizeInfo.setRemark(e.getMessage());
			onSyncError(synchronizeInfo);
			throw new NiceException("同步"+desc+"给节点["+remoteNode.getNodeName()+"]失败，请手动同步！");
		}
		if(SUCCESSFAIL.SUCCESS.getStatus().equals(response.getCode())){
			logger.info("同步"+desc+"给节点["+remoteNode.getNodeName()+"]成功");
			onSyncSuccess(synchronizeInfo);
		}else{
			//插入同步表，然后管理员在界面上手动同步
			synchronizeInfo.setRemark(response.getMsg());
			onSyncError(synchronizeInfo);
			logger.error("同步"+desc+"给节点["+remoteNode.getNodeName()+"]失败，请手动在界面上同步！，错误信息："+response.getMsg());
			throw new NiceException("同步"+desc+"给节点["+remoteNode.getNodeName()+"]失败，请手动同步！");
		}
	}
	/**
	 * 同步失败，插入数据库，界面上手动发
	 * @param synchronizeInfo
	 * @throws ServiceException 
	 */
	private void onSyncError(SynchronizeInfo synchronizeInfo) throws ServiceException {
		synchronizeInfo.setStatus(SyncStatus.FAIL.getCode());
		communicateService.insertOrUpdateSynchronizeInfo(synchronizeInfo);
	}
	/**
	 * 同步成功后的处理
	 * @param synchronizeInfo
	 * @throws ServiceException 
	 */
	private void onSyncSuccess(SynchronizeInfo synchronizeInfo) throws ServiceException {
		synchronizeInfo.setStatus(SyncStatus.SUCCESS.getCode());
		communicateService.insertOrUpdateSynchronizeInfo(synchronizeInfo);
	}
	public void checkInstallUrl(String nodeInstallUrl) throws NiceException {
		
		//不能和自己建立关系
		if(communicateService.isSelfUrl(nodeInstallUrl)){
			throw new NiceException("请求地址错误，交换节点不能是本地节点的地址！");
		}
		//不能是localhost之类的
		String ip;
		try {
			ip = UrlUtil.getIpFromUrl(nodeInstallUrl);
		} catch (Exception e) {
			throw new NiceException("请求地址错误！");
		}
		if(AppConst.localIpStyle.contains(ip)){
			throw new NiceException("请求地址错误,不能是本地ip！");
		}
		if(!communicateService.syncUrlIsValid(nodeInstallUrl)){
			throw new NiceException("请求地址错误！");
		}
	}

    public static NodeCommunicateInterface getRemoteNodeCommunicateService(String nodeInstallUrl){
    	   //默认webservice通道同步
    		JaxWsProxyFactoryBean bean = new JaxWsProxyFactoryBean();
    		bean.setAddress(NodeCommunicateService.getSyncUrl(nodeInstallUrl));
    		bean.setServiceClass(NodeCommunicateInterface.class);
    		return bean.create(NodeCommunicateInterface.class);
    }
}
